﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using MapDownloader.Core.Core;
using MapDownloader.Core.Models;
using MapDownloader.Core.Storage;

namespace MapDownloader.Core.MapProviders
{
   public abstract class BaseMapProvider
   {
      /// <summary>
      ///    MapProvider的名称
      /// </summary>
      protected abstract string Name { get; }

      /// <summary>
      ///    图片存储代理
      /// </summary>
      public static BaseImageProxy TileImageProxy { get; set; }

      /// <summary>
      ///    存储器
      /// </summary>
      public ITileStore Storager { get; set; }

      /// <summary>
      ///    最小zoom
      /// </summary>
      public int MinZoom { get; set; } = 1;

      /// <summary>
      ///    最大zoom
      /// </summary>
      public int MaxZoom { get; set; } = 19;

      /// <summary>
      ///    使用内存缓存地图
      /// </summary>
      public bool UseMemoryCache = true;

      #region 下载瓦片相关

      /// <summary>
      ///    http访问的NetworkCredential
      /// </summary>
      public static ICredentials Credential;

      /// <summary>
      ///    User-agent HTTP header.
      ///    It's pseudo-randomized to avoid blockages...
      /// </summary>
      public static string UserAgent = string.Format(
         "Mozilla/5.0 (Windows NT {1}.0; {2}rv:{0}.0) Gecko/20100101 Firefox/{0}.0",
         Stuff.random.Next(DateTime.Today.Year - 1969 - 5, DateTime.Today.Year - 1969),
         Stuff.random.Next(0, 10) % 2 == 0 ? 10 : 6,
         Stuff.random.Next(0, 10) % 2 == 1 ? string.Empty : "WOW64; ");

      /// <summary>
      ///    连接超时时间
      /// </summary>
      public static int TimeoutMs = 5 * 1000;

      /// <summary>
      ///    Referer HTTP header.
      /// </summary>
      public string RefererUrl = string.Empty;

      private static readonly string requestAccept = "*/*";
      private static readonly string responseContentType = "image";

      /// <summary>
      ///    Token
      /// </summary>
      public static string Authorization = string.Empty;

      #endregion
      /// <summary>
      ///    构造函数
      /// </summary>
      protected BaseMapProvider()
      {
         TileImageProxy = new DefaultImageProxy();
         Storager = new MySqlTileStorage();
      }

      /// <summary>
      ///    获取瓦片
      /// </summary>
      /// <param name="pos">瓦片坐标</param>
      /// <returns></returns>
      public abstract BaseImage GetTileImage(TilePoint pos);
      /// <summary>
      ///    检查返回的连接中是否存在瓦片
      /// </summary>
      /// <param name="response"></param>
      /// <returns></returns>
      protected virtual bool CheckTileImageHttpResponse(WebResponse response)
      {
         return response.ContentType.Contains(responseContentType);
      }

      private WebRequest SettingHttp(string url)
      {
         var request = WebRequest.Create(url);

         if (Credential != null)
         {
            request.PreAuthenticate = true;
            request.Credentials = Credential;
         }

         if (!string.IsNullOrEmpty(Authorization)) request.Headers.Set("Authorization", Authorization);

         if (request is HttpWebRequest)
         {
            var r = request as HttpWebRequest;
            r.UserAgent = UserAgent;
            r.ReadWriteTimeout = TimeoutMs * 6;
            r.Accept = requestAccept;
            r.Referer = RefererUrl;
            r.Timeout = TimeoutMs;
         }
         return request;
      }
      /// <summary>
      ///    使用http下载瓦片
      /// </summary>
      /// <param name="url"></param>
      /// <returns></returns>
      protected BaseImage GetTileUsingHttp(string url)
      {
         BaseImage result = null;
         var request = SettingHttp(url);
         using (var response = request.GetResponse())
         {
            if (CheckTileImageHttpResponse(response))
            {
               using (var responseStream = response.GetResponseStream())
               {
                  var data = Stuff.CopyStream(responseStream, false);
                  if (data.Length > 0)
                  {
                     result = TileImageProxy.FromStream(data);
                     if (result != null)
                     {
                        result.Data = data;
                        result.Data.Position = 0;
                     }
                     else
                     {
                        data.Dispose();
                     }
                  }
               }
            }
            response.Close();
         }
         return result;
      }

      /// <summary>
      ///    从服务器获取数据
      /// </summary>
      /// <param name="url"></param>
      /// <returns></returns>
      protected string GetContentUsingHttp(string url)
      {
         var result=string.Empty;
         var request = SettingHttp(url);
         using (var response = request.GetResponse())
         {
            using (var responseStream = response.GetResponseStream())
            {
               if (responseStream == null)
                  return result;
               using (var read = new StreamReader(responseStream, Encoding.UTF8))
               {
                  result = read.ReadToEnd();
               }
            }
            response.Close();
         }

         return result;
      }
      /// <summary>
      /// ToString
      /// </summary>
      /// <returns></returns>
      public override string ToString()
      {
         return $"地图提供商为:{Name}";
      }
      /// <summary>
      /// 从地图代理商获取其行政区域
      /// </summary>
      /// <param name="area">行政区域名称</param>
      /// <returns></returns>
      protected abstract List<List<LatLngPoint>> GetBoundary(string area);
      /// <summary>
      /// 获取边界区域所在的矩形的左上角和右下角坐标
      /// </summary>
      /// <param name="area"></param>
      /// <returns></returns>
      public virtual List<(LatLngPoint, LatLngPoint)> GetRectPointLatLngFromPoints(string area)
      {
         List<(LatLngPoint, LatLngPoint)> result = new List<(LatLngPoint, LatLngPoint)>();
         var boundarys = GetBoundary(area);
         foreach (var item in boundarys)
         {
            result.Add(GetRectPonitLatLngFromPoints(item));
         }

         return result;
      }
      private (LatLngPoint, LatLngPoint) GetRectPonitLatLngFromPoints(List<LatLngPoint> points)
      {
         var maxLat = 0d;
         var minLat = double.MaxValue;
         var maxLng = 0d;
         var minLng = double.MaxValue;
         foreach (var item in points)
         {
            if (item.Lat > maxLat)
            {
               maxLat = item.Lat;
            }
            else
            {
               if (item.Lat < minLat)
                  minLat = item.Lat;
            }
            if (item.Lng > maxLng)
            {
               maxLng = item.Lng;
            }
            else
            {
               if (item.Lng < minLng)
                  minLng = item.Lng;
            }
         }
         return (new LatLngPoint(minLat, minLng), new LatLngPoint(maxLat, maxLng));
      }

      /// <summary>
      /// 获取给定的区域访问内的瓦片坐标
      /// </summary>
      /// <param name="minPointLatLng">左上角</param>
      /// <param name="maxPointLatLng">右下角</param>
      /// <returns></returns>
      public abstract List<StorageQueueItem> GetTilePoint(LatLngPoint minPointLatLng, LatLngPoint maxPointLatLng);
      /// <summary>
      /// 获取多边形内所有的瓦片坐标
      /// </summary>
      /// <param name="area"></param>
      /// <returns></returns>
      public List<StorageQueueItem> GetTilePointItem(string area)
      {
         var rects = GetRectPointLatLngFromPoints(area);
         if (rects.Count > 0)
         {
            var result = new List<StorageQueueItem>();
            foreach (var item in rects)
            {
               var tem = GetTilePoint(item.Item1, item.Item2);
               result.AddRange(tem);
            }

            return result;
         }
         return new List<StorageQueueItem>();
      }
      /// <summary>
      /// 获取多边的瓦片坐标
      /// </summary>
      /// <param name="minLatLngPoint"></param>
      /// <param name="maxLatLngPoint"></param>
      /// <returns></returns>
      public List<StorageQueueItem> GetTilePointItem(LatLngPoint minLatLngPoint, LatLngPoint maxLatLngPoint)
      {
         
         return GetTilePoint(minLatLngPoint, maxLatLngPoint);
      }
   }
}